<?php
/**
 * cookie定义
 *
 * @since 1.0
 * @package if
 * @subpackage controller.context
 */
class ICookie extends IObject  {
	private $name;
	private $value;
	private $path;
	private $secure;
	private $domain;
	private $maxAge;
	private $httponly = false;
	private $protected = false;
	
	/**
	 * 构造器
	 * 
	 * 从1.1.0开始，$value可以是一个数组，包含被设置的cookie的参数：
	 * <code>
	 * 	$cookie = new ICookie("name", array(
	 * 		"define" => true,
	 * 		"value" => "1",
	 * 		"path" => "/",
	 *		"secure" => 0,
	 * 		"domain" => "mydomain.com",
	 * 		"maxAge" => 3600
	 *  ));
	 * </code>
	 * 其中的define和value参数必不可少，否则会将当前数组视为cookie的值
	 * 
	 * 从1.1.0开始起，如果cookie的名称中有点(.)符号，则视为设置当前cookie值为数组。从该
	 * 版本起可以设置一个cookie值为多级数组。
	 *
	 * @param string $name cookie名
	 * @param string $value cookie值
	 */
	function __construct($name, $value) {
		if (is_array($value) && isset($value["define"]) && $value["define"] && array_key_exists("value", $value)) {
			foreach (array("path", "secure", "domain", "maxAge", "httponly", "protected") as $prop) {
				if (isset($value[$prop])) {
					$this->set($prop, $value[$prop]);
				}
			}
			$value = $value["value"];
		}
		
		if (strstr($name, ".")) {
			$names = explode(".", $name);
			$name = array_shift($names);
			$value = if_array_set(array(), $names, $value);
		}
		$this->name = $name;
		$this->value = $value;
	}
	
	/**
	 * 设置名称
	 *
	 * @param string $name 名称
	 */
	function setName($name) {
		$this->name = $name;
	}
	
	/**
	 * 取得名称
	 *
	 * @return string
	 */
	function name() {
		return $this->name;
	}
	
	/**
	 * 设置值
	 *
	 * @param scalar $value 设置值
	 */
	function setValue($value) {
		$this->value = $value;
	}
	
	/**
	 * 取得值
	 *
	 * @return string
	 */
	function value() {
		return strval($this->value);
	}
	
	/**
	 * 设置path
	 *
	 * @param string $path 有效的路径
	 */
	function setPath($path) {
		$this->path = $path;
	}
	
	/**
	 * 取得path
	 *
	 * @return string
	 */
	function path() {
		return $this->path;
	}
	
	/**
	 * 设置是否为安全连接
	 *
	 * @param integer $secure 是否为安全连接（0或1）
	 */
	function setSecure($secure) {
		$this->secure = $secure;
	}
	
	/**
	 * 判断是否为安全连接
	 *
	 * @return integer
	 */
	function secure() {
		return $this->secure;
	}
	
	/**
	 * 设置有效域
	 *
	 * @param string $domain 域
	 */
	function setDomain($domain) {
		$this->domain = $domain;
	}
	
	/**
	 * 取得有效域
	 *
	 * @return string
	 */
	function domain() {
		return $this->domain;
	}
	
	/**
	 * 设置生存时间
	 * 
	 * 默认的是随浏览器的关闭而消失，但目前知道firefox可以设置关闭时不删除cookie。
	 *
	 * @param integer $maxAge 生存时间
	 */
	function setMaxAge($maxAge) {
		$this->maxAge = $maxAge;
	}
	
	/**
	 * 取得生存期
	 *
	 * @return integer
	 */
	function maxAge() {
		return $this->maxAge;
	}
	
	/**
	 * 将当前cookie发送到浏览器
	 * 
	 * 从 1.1.0 开始，如果设置cookie的值为null，则视为删除当前cookie
	 *
	 * @since 1.0
	 * @return boolean
	 */
	function send() {
		if (is_null($this->value)) {
			return $this->delete();
		}
		if (is_array(self::get($this->name))) {
			$this->delete();
		}
		$maxAge = $this->maxAge;
    	if ($maxAge != 0) {
    		$maxAge += $this->_time();
    	}
    	$value = $this->value;
    	if ($this->protected) {
			$value = self::_encryptValue($value);
		}
    	$_COOKIE[$this->name] = $value;
		return $this->_setCookie($this->name, $value, $maxAge);
	}
	
	private static function _encryptValue($value) {
		$ip = ip2long(IRequest::singleton()->ip());
		if (is_array($value)) {
			$value["__i"] = $ip;
		}
		else {
			$value = $ip . "," . $value;
		}
		return IApplication::singleton()->encrypt($value);
	}
	
	private static function _decryptValue($value) {
		$ip = ip2long(IRequest::singleton()->ip());
		$value = @IApplication::singleton()->decrypt($value);
		if (is_array($value)) {
			if (!isset($value["__i"]) || $value["__i"] != $ip) {
				return;
			}
			return $value;
		}
		if (!strstr($value, ",")) {
			return;
		}
		list($i, $value) = explode(",", $value, 2);
		if ($i != $ip) {
			return;
		}
		return $value;
	}
	
	private function _setCookie($name, $value, $maxAge) {
		if (!is_array($value)) {
			return setcookie($name, $value, $maxAge, $this->path, $this->domain, $this->secure, $this->httponly);
		}
		else {
			$bool = true;
			foreach ($value as $_name => $_value) {
				if (!$this->_setCookie($name . "[" . $_name . "]", $_value, $maxAge)) {
					$bool = false;
				}
			}
			return $bool;
		}
	}
	
	/**
	 * 删除当前cookie
	 *
	 * @return boolean
	 * @since 1.1.0
	 */
	function delete() {
		$past = time() -3600 * 24 * 365;//防止客户端事件和服务器时间不一致
		$value = self::get($this->name);
		if (is_null($value)) {
			return true;
		}
		unset($_COOKIE[$this->name]);
		return $this->_setCookie($this->name, $value, $past);
	}

	/**
	 * 转换为字符串
	 *
	 * @return string
	 * @since 1.0
	 */
	function __toString() {
		return is_array($this->value) ? implode(", ", $this->value) : strval($this->value);
	}
	
	/**
	 * 取得cookie值
	 *
	 * 从1.1.0起支持用点（.）表示多级数组
	 * 
	 * @param string $name cookie名
	 * @return string|null|array
	 * @since 1.0
	 */
	static function get($name, $protected = false) {
		$value = if_array_get($_COOKIE, $name);
		if ($protected) {
			if (is_string($value)) {
				return self::_decryptValue($value);
			}
			return null;
		}
		return $value;
	}
	
	/**
	 * 取得所有的cookie值
	 *
	 * @return array
	 * @since 1.0
	 */
	static function all() {
		return $_COOKIE;
	}
	
	/**
	 * 设置是否仅能通过HTTP协议访问
	 *
	 * @param boolean $httponly true或false
	 * @since 1.1.0
	 */
	function setHttponly($httponly = true) {
		$this->httponly = $httponly;
	}
	
	/**
	 * 判断当前cookie是否仅能通过HTTP协议访问
	 *
	 * @return boolean
	 * @since 1.1.0
	 */
	function isHttponly() {
		return $this->httponly;
	}
	
	/**
	 * 设置是否保护当前cookie值不被修改
	 * 
	 * 如果设置了数据保护，就会生成一个新的值在客户端，客户端一旦通过软件或者其他工具修改了此值，通过
	 * <code>
	 * $value = ICookie::get($name, true);
	 * </code>
	 * 访问此cookie时，值就是无效的
	 *
	 * @param boolean $protected 是否保护数据
	 * @since 1.1.0
	 */
	function setProtected($protected = true) {
		$this->protected = $protected;
	}
	
	/**
	 * 判断当前cookie是否设置了数据保护
	 *
	 * @return boolean
	 * @since 1.1.0
	 */
	function isProtected() {
		return $this->protected;
	}
	
	/**
	 * 设置属性值
	 *
	 * @param string $name 属性名
	 * @param mixed $value 属性值
	 * @since 1.1.0
	 */
	function set($name, $value) {
		$name = strtolower($name);
		if (in_array($name, array("name", "value", "path", "secure", "domain", "maxAge", "httponly", "protected"))) {
			$method = "set" . $name;
			if (method_exists($method, $this)) {
				$this->$method($value);
			}
		}
	}
	
	/**
	 * 从一个字符串中分析出一个Cookie对象
	 *
	 * @param string $str 字符串，类似于a=1; expires=Wed, 11-Feb-2009 17:36:11 GMT; path=/
	 * @return ICookie
	 * @since 1.1.0
	 */
	static function parse($str) {
		if (!$str) {
			return null;
		}
		$pairs = explode(";", $str);
		list($name, $value) = explode("=", $pairs[0], 2);
		$cookie = new ICookie($name, $value);
		$count = count($pairs);
		for ($i = 1; $i < $count; $i ++) {
			if (strstr($pairs[$i], "=")) {
				list($_name, $_value) = explode("=", $pairs[$i], 2);
				$_name = trim($_name);
				if (strtolower($_name) == "expires" && $_value) {
					$time = strtotime($_value);
					$cookie->setMaxAge($time - time());
				}
				else {
					$cookie->set($_name, $_value);
				}
			}
			else {
				$cookie->set($pairs[$i], true);
			}
		}
		return $cookie;
	}
}

?>